import { Material } from "./Material";
import { any, has } from "../core/logic";
import { Buffer } from "../gpu/types/Buffer";
import { textureLoader } from "../assets/loader/TextureLoader";
import { color } from "../core/color";
import { Engine } from "../Engine";
/**
 * 材质类。
 * @class
 * @memberof w.material
 */
class StandardMaterial extends Material {
  /**
   * 创建基础材质
   * @constructor
   * @param {*} opts
   * @param {String} opts.map【可选，缺省:undefine】基地色贴图。
   * @param {String} opts.emissiveMap【可选，缺省:undefine】自发光贴图。
   * @param {String} opts.metRougMap【可选，缺省:undefine】金属粗糙度贴图。
   * @param {String} opts.normalMap【可选，缺省:undefine】法向量贴图。
   * @param {String} opts.aoMap【可选，缺省:undefine】ao贴图。
   * @param {Number} opts.aoMapIntensity【可选，缺省:1.0】ao强度。
   * @param {Number} opts.metalness【可选，缺省:1.0】金属度。
   * @param {Number} opts.roughness【可选，缺省:1.0】粗糙度。
   * @param {Number} opts.refraction【可选，缺省:1.5】折射率。
   * @param {Number} opts.transmission【可选，缺省:0.0】折射系数。
   * @param {String} opts.color 【可选，缺省:"#ffffff"】基地色。
   * @param {String} opts.emissive 【可选，缺省:"#000000"】自发光颜色。
   * @param {Boolean} opts.mipMap 【可选，缺省:false】是否生成mipMap。
   */
  constructor(opts = {}) {
    super(opts);
    this.materialType = "StandardMaterial";
    this.map = opts.map;
    this.emissiveMap = opts.emissiveMap;
    this.metRougMap = opts.metRougMap;
    this.normalMap = opts.normalMap;
    this.aoMap = opts.aoMap;

    this.aoMapIntensity = any(opts.aoMapIntensity, 1.0);
    this.metalness = any(opts.metalness, 1.0); //金属度
    this.roughness = any(opts.roughness, 1.0); //粗糙度
    this.refraction = any(opts.refraction, 1.5); //折射率
    this.transmission = any(opts.transmission, 0.0); //折射系数
    this.color = any(opts.color, "#ffffff");
    this.emissive = any(opts.emissive, "#000000");
    this.specularColorFactor = opts.specularColorFactor;
    this.specularFactor = opts.specularFactor;
    this.ior = opts.ior;
    this.autoUpdate = true;
    this.update();
  }
  get materialType() {
    return this._materialType;
  }
  set materialType(v) {
    this._materialType = v;
    this.addConfigParam("materialType", v);
    this.update();
    return this;
  }
  set map(v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, "map");
    this.registerTexture(v, "map", t => {
      this._map = t;
      this.setState(true, "map");
      this.addMapParam("map");
      if (this.has("map")) {
        const texture = this.map;
        // const texture= await textureObject.toGPU();
        this.textureToGPU(texture, "map");
        // textureLoader.textureMap.delete(this.map)
      }
      this.update();
    });
    return this;
  }
  get map() {
    return this._map;
  }
  set emissiveMap(v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, "emissiveMap");
    this.registerTexture(v, "emissiveMap", t => {
      this._emissiveMap = t;
      this.setState(true, "emissiveMap");
      this.addMapParam("emissiveMap");
      if (this.has("emissiveMap")) {
        const texture = this.emissiveMap;
        // const texture= await textureObject.toGPU();
        this.textureToGPU(texture, "emissiveMap");
        // textureLoader.textureMap.delete(this.emissiveMap)
      }
      this.update();
    });
    return this;
  }
  get emissiveMap() {
    return this._emissiveMap;
  }
  set metRougMap(v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, "metRougMap");
    this.registerTexture(v, "metRougMap", t => {
      this._metRougMap = t;
      this.setState(true, "metRougMap");
      this.addMapParam("metRougMap");
      if (this.has("metRougMap")) {
        const texture = this.metRougMap;
        // const texture= await textureObject.toGPU();
        this.textureToGPU(texture, "metRougMap");
        // textureLoader.textureMap.delete(this.metRougMap)
      }
      this.update();
    });
    return this;
  }
  get metRougMap() {
    return this._metRougMap;
  }
  set normalMap(v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, "normalMap");
    this.registerTexture(v, "normalMap", t => {
      this._normalMap = t;
      this.setState(true, "normalMap");
      this.addMapParam("normalMap");
      if (this.has("normalMap")) {
        const texture = this.normalMap;
        // const texture= await textureObject.toGPU();
        this.textureToGPU(texture, "normalMap");
        // textureLoader.textureMap.delete(this.normalMap)
      }
      this.update();
    });
    return this;
  }
  get normalMap() {
    return this._normalMap;
  }
  set aoMap(v) {
    if (!has(v)) {
      return;
    }
    this.setState(false, "aoMap");
    this.registerTexture(v, "aoMap", t => {
      this._aoMap = t;
      this.setState(true, "aoMap");
      this.addMapParam("aoMap");
      if (this.has("aoMap")) {
        const texture = this.aoMap;
        // const texture= await textureObject.toGPU();
        this.textureToGPU(texture, "aoMap");
        // textureLoader.textureMap.delete(this.aoMap)
      }
      this.update();
    });
    return this;
  }
  get aoMap() {
    return this._aoMap;
  }
  set aoMapIntensity(v) {
    this._aoMapIntensity = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get aoMapIntensity() {
    return this._aoMapIntensity;
  }
  set metalness(v) {
    this._metalness = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get metalness() {
    return this._metalness;
  }
  set roughness(v) {
    this._roughness = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get roughness() {
    return this._roughness;
  }
  set refraction(v) {
    this._refraction = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get refraction() {
    return this._refraction;
  }
  set transmission(v) {
    this._transmission = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get transmission() {
    return this._transmission;
  }
  set color(v) {
    this._color = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get color() {
    return this._color;
  }
  set emissive(v) {
    this._emissive = v;
    this.addParam("material");
    this.update();
    return this;
  }
  get emissive() {
    return this._emissive;
  }
  set specularColorFactor(v) {
    if (!v) return;
    this._specularColorFactor = v;
    this.addParam("specularColorFactor");
    this.update();
    return this;
  }
  get specularColorFactor() {
    return this._specularColorFactor;
  }
  set specularFactor(v) {
    if (!v) return;
    this._specularFactor = v;
    this.addParam("specularFactor");
    this.update();
    return this;
  }
  get specularFactor() {
    return this._specularFactor;
  }
  set ior(v) {
    if (!v) return;
    this._ior = v;
    this.addParam("ior");
    this.update();
    return this;
  }
  get ior() {
    return this._ior;
  }
  updateState() {
    let state =
      (this.getState("map") || this.getState("map") == undefined) &&
      (this.getState("emissiveMap") || this.getState("emissiveMap") == undefined) &&
      (this.getState("metRougMap") || this.getState("metRougMap") == undefined) &&
      (this.getState("normalMap") || this.getState("normalMap") == undefined) &&
      (this.getState("aoMap") || this.getState("aoMap") == undefined);
    this.setState(state);
  }
  update() {
    if (!this.autoUpdate) {
      return;
    }
    if (!this.getState()) {
      this.updateState();
    }
    if (!this.getState()) {
      return;
    }
    let oldhash = this.hash;
    this.updateHash();
    if (oldhash === this.hash) {
      if (!this.bindGroup2) {
        this.setState(true, "layout");
      } else {
        this.setState(false, "layout");
      }
    } else {
      this.setState(true, "layout");
    }
    this.notice();
  }
  toGPU() {
    super.toGPU();
    if (this.has("material")) {
      let matreialParameter = this.getParam("material");
      if (matreialParameter) {
        if (!matreialParameter.state) {
          let data = new Float32Array(16);
          data.set([this.opacity], 0);
          data.set([this.aoMapIntensity], 1);
          data.set([this.metalness], 2);
          data.set([this.roughness], 3);
          data.set([this.refraction], 4);
          data.set([this.transmission], 5);
          data.set([this.defuseLight ? 1.0 : 0.0], 6);
          data.set(color.format(this.color, true), 8);
          data.set(color.format(this.emissive, true), 12);
          matreialParameter.buffer(data, 0);
        }
      } else {
        let data = new Float32Array(20);
        data.set([this.opacity], 0);
        data.set([this.aoMapIntensity], 1);
        data.set([this.metalness], 2);
        data.set([this.roughness], 3);
        data.set([this.refraction], 4);
        data.set([this.transmission], 5);
        data.set([this.defuseLight ? 1.0 : 0.0], 6);
        data.set(color.format(this.color, true), 8);
        data.set(color.format(this.emissive, true), 12);
        matreialParameter = new Buffer({
          name: "standardMaterialProperties",
          usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
          visibility: GPUShaderStage.FRAGMENT,
          type: "uniform",
          size: 80,
          data,
        });
        this.addParam("material", matreialParameter);
      }
      matreialParameter.state = true;
    }
    if (this.has("toneMappingExposure")) {
      this.bufferToGPU(new Float32Array([this.toneMappingExposure]), "toneMappingExposure");
    }
    if (this.has("specularColorFactor") || this.has("specularFactor")) {
      let specularColorFactor = this.specularColorFactor || [1.0, 1.0, 1.0];
      let specularFactor = this.specularFactor || 1.0;

      this.bufferToGPU(color.format(specularColorFactor, false), "specularColorFactor");
      this.bufferToGPU(new Float32Array([specularFactor]), "specularFactor");
    }
    if (this.has("ior")) {
      this.bufferToGPU(new Float32Array([this.ior]), "ior");
    }
    // 生成bindGroup和groupLayout
    if (this.getState("layout")) {
      const params = this.getAllParams();
      let entriesGroup2 = [],
        entriesLayout2 = [];
      let entriesGroup3 = [],
        entriesLayout3 = [];
      this.defs = {};
      this.config = {};
      for (let index = 0; index < params.length; index++) {
        const param = params[index];
        if (!param) continue;
        this.defs[param.name] = param.value;
        if (param.group === 2) {
          entriesGroup2.push(param.gpu.entry);
          entriesLayout2.push(param.gpu.layout);
        } else if (param.group === 3) {
          entriesGroup3.push(param.gpu.entry);
          entriesLayout3.push(param.gpu.layout);
        } else {
          this.config[param.name] = param.value;
        }
      }
      // console.log(entriesLayout2,"entriesLayout2",this.defs);
      // 生成layout与bindgroup
      if (entriesGroup2.length > 0) {
        this.grouplayouts2 = Engine.instance.device.createBindGroupLayout({
          label: "layout" + 2,
          entries: entriesLayout2,
        });
        this.bindGroup2 = Engine.instance.device.createBindGroup({
          label: "bindGroup" + 2,
          layout: this.grouplayouts2,
          entries: entriesGroup2,
        });
      }
      if (entriesGroup3.length > 0) {
        this.grouplayouts3 = Engine.instance.device.createBindGroupLayout({
          label: "layout" + 3,
          entries: entriesLayout3,
        });
        this.bindGroup3 = Engine.instance.device.createBindGroup({
          label: "bindGroup" + 3,
          layout: this.grouplayouts3,
          entries: entriesGroup3,
        });
      }
      this.setState(false, "layout");
    }
  }
}
export { StandardMaterial };
